﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace CarBloke.Controls
{
    public partial class GraphControl : UserControl
    {
        private double _marginX = 70;
        private double _marginY = 40;
        private double _tickLenth = 10;

        private readonly Graph _graph;
        Double _minXAxisValue = Double.NaN;
        Double _maxXAxisValue = Double.NaN;
        Double _minYAxisValue = Double.NaN;
        Double _maxYAxisValue = Double.NaN;

        Double _graphWidthValue = Double.NaN;
        Double _graphHeightValue = Double.NaN;

        public GraphControl(Graph graph, String xLabel)
        {
            InitializeComponent();

            if (graph.Data.Count < 4)
            {
                DataNeededTextBlock.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                DataNeededTextBlock.Visibility = System.Windows.Visibility.Collapsed;
                GraphLabelTextBlock.Text = xLabel;
                _graph = graph;
                initializeGraph();
                this.SizeChanged += new SizeChangedEventHandler(GraphControl_SizeChanged);
            }
        }

        private void initializeGraph()
        {
            foreach (AxisMarker axisMarker in _graph.xAxisMarkers)
            {
                if (Double.IsNaN(_minXAxisValue))
                {
                    _minXAxisValue = axisMarker.Value;
                }
                else
                {
                    _minXAxisValue = Math.Min(axisMarker.Value, _minXAxisValue);
                }

                if (Double.IsNaN(_maxXAxisValue))
                {
                    _maxXAxisValue = axisMarker.Value;
                }
                else
                {
                    _maxXAxisValue = Math.Max(axisMarker.Value, _maxXAxisValue);
                }
            }

            foreach (AxisMarker axisMarker in _graph.yAxisMarkers)
            {
                if (Double.IsNaN(_minYAxisValue))
                {
                    _minYAxisValue = axisMarker.Value;
                }
                else
                {
                    _minYAxisValue = Math.Min(axisMarker.Value, _minYAxisValue);
                }

                if (Double.IsNaN(_maxYAxisValue))
                {
                    _maxYAxisValue = axisMarker.Value;
                }
                else
                {
                    _maxYAxisValue = Math.Max(axisMarker.Value, _maxYAxisValue);
                }
            }

            _graphWidthValue = _maxXAxisValue - _minXAxisValue;
            _graphHeightValue = _maxYAxisValue - _minYAxisValue;
        }

        void GraphControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            LayoutRoot.Width = this.Width;
            LayoutRoot.Height = this.Height;

            draw();
        }

        private void draw()
        {
            clear();

            plotData();
            drawAxis();            
        }       

        private void clear()
        {
            LayoutRoot.Children.Clear();
        }

        private void drawAxis()
        {
            Double graphWidthPixels = LayoutRoot.ActualWidth;
            Double graphHeightPixels = LayoutRoot.ActualHeight;

            Line xAxisInnerLine = new Line();
            xAxisInnerLine.X1 = _marginX;
            xAxisInnerLine.Y1 = graphHeightPixels - _marginY;
            xAxisInnerLine.X2 = graphWidthPixels - _marginX;
            xAxisInnerLine.Y2 = graphHeightPixels - _marginY;

            xAxisInnerLine.Stroke = (Brush)Application.Current.Resources["PhoneContrastBackgroundBrush"];
            xAxisInnerLine.StrokeThickness = 2;
            LayoutRoot.Children.Add(xAxisInnerLine);            

            Line yAxisLine = new Line();
            yAxisLine.X1 = _marginX;
            yAxisLine.Y1 = graphHeightPixels - _marginY;
            yAxisLine.X2 = _marginX;
            yAxisLine.Y2 = _marginY;

            yAxisLine.StrokeThickness = 2;
            yAxisLine.Stroke = (Brush)Application.Current.Resources["PhoneContrastBackgroundBrush"];
            LayoutRoot.Children.Add(yAxisLine);


            foreach (AxisMarker axisMarker in _graph.xAxisMarkers)
            {
                Double valuePercentage = (axisMarker.Value - _minXAxisValue) / _graphWidthValue;
                Double xPixel = valuePercentage * (graphWidthPixels - _marginX * 2) + _marginX;

                if (axisMarker.TickType == AxisMarker.TickTypes.Large)
                {
                    Line axisNotch = new Line();
                    axisNotch.X1 = xPixel;
                    axisNotch.Y1 = graphHeightPixels - (_marginY - _tickLenth / 2);
                    axisNotch.X2 = xPixel;
                    axisNotch.Y2 = graphHeightPixels - (_marginY + _tickLenth / 2);

                    axisNotch.StrokeThickness = 3;
                    axisNotch.Stroke = (Brush)Application.Current.Resources["PhoneContrastBackgroundBrush"];
                    LayoutRoot.Children.Add(axisNotch);
                }

                TextBlock label = new TextBlock();
                label.Text = axisMarker.Label;
                label.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
                label.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                label.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                label.Margin = new Thickness(xPixel - label.ActualWidth, 0, 0, 5);
                LayoutRoot.Children.Add(label);
            }

            foreach (AxisMarker axisMarker in _graph.yAxisMarkers)
            {
                Double valuePercentage = (axisMarker.Value - _minYAxisValue) / _graphHeightValue;
                Double yPixel = graphHeightPixels - (valuePercentage * (graphHeightPixels - _marginY * 2) + _marginY);

                if (axisMarker.TickType == AxisMarker.TickTypes.Large)
                {
                    Line axisNotch = new Line();
                    axisNotch.X1 = _marginX - _tickLenth / 2;
                    axisNotch.Y1 = yPixel;
                    axisNotch.X2 = _marginX + _tickLenth / 2;
                    axisNotch.Y2 = yPixel;

                    axisNotch.StrokeThickness = 3;
                    axisNotch.Stroke = (Brush)Application.Current.Resources["PhoneContrastBackgroundBrush"];
                    LayoutRoot.Children.Add(axisNotch);
                }

                TextBlock label = new TextBlock();
                label.Text = axisMarker.Label;
                label.VerticalAlignment = System.Windows.VerticalAlignment.Top;
                label.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                label.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                label.Margin = new Thickness(5, yPixel - label.ActualHeight, 0, 0);
                LayoutRoot.Children.Add(label);
            }
        }

        private void plotData()
        {
            Double graphWidthPixels = LayoutRoot.ActualWidth - 2 * _marginX;
            Double graphHeightPixels = LayoutRoot.ActualHeight - 2 * _marginY;

            Path plotLinePath = new Path();
            plotLinePath.StrokeThickness = 6;           
            plotLinePath.StrokeEndLineCap = PenLineCap.Round;
            plotLinePath.StrokeStartLineCap = PenLineCap.Round;
            plotLinePath.StrokeLineJoin = PenLineJoin.Round;
            plotLinePath.Stroke = (Brush)Application.Current.Resources["PhoneAccentBrush"];

            PathGeometry plotLinePathGeometry = new PathGeometry();            
            PathFigure plotLinePathFigure = new PathFigure();
            plotLinePathGeometry.Figures.Add(plotLinePathFigure);
            plotLinePath.Data = plotLinePathGeometry;

            bool firstPoint = true;
            foreach (OrderedPair<Double, Double> graphPoint in _graph.Data.OrderBy((pt) => pt.XValue))
            {
                Double pixelX = ((graphPoint.XValue - _minXAxisValue) / _graphWidthValue) * graphWidthPixels + _marginX;
                Double pixelY = graphHeightPixels - (((graphPoint.YValue - _minYAxisValue) / _graphHeightValue) * graphHeightPixels) + _marginY;
                Point pixelPoint = new Point(pixelX, pixelY);

                if (firstPoint)
                {
                    plotLinePathFigure.StartPoint = pixelPoint;
                    firstPoint = false;
                }
                else
                {
                    LineSegment segment = new LineSegment();
                    segment.Point = pixelPoint;
                    plotLinePathFigure.Segments.Add(segment);
                }
            }

            LayoutRoot.Children.Add(plotLinePath);
        }
    }
}
